/*
HEADER
 */
/* Reform paragraphs.  Dot commands in the text are interpreted and
 * left in the result.  Adapted from CUG .. 3/15/87 G. Osborn.
 *
 * All automatic pagination output is suppressed until a .he command
 * is processed.  The text portion of the .he command may be null.
 *
 * dot commands to not contribute to the line count if left
 * in the output.
 */

#include <ctype.h>
#include <stdio.h>
#include "ged.h"



#define PAGEWIDTH 72    /* default page width */
#define PAGELINES  66   /* default page length */
#define PAGECHAR '#'    /* page number escape char */

static int fill;                 /* in fill mode if YES */
static int lsval;                /* current line spacing */
static int inval;                /* current indent; >= 0 */
static int rmval;                /* current right margin */
static int ceval;                /* number of lines to center */
static int ulval;                /* number of lines to underline */
static int curpag;               /* current output page number */
static int newpag;               /* next output page number */
static int pageline;             /* next line to be printed */
static int plval;                /* page length in lines */
static int m1val;                /* top margin, including header */
static int m2val;                /* margin after header */
static int m3val;                /* margin after last text line */
static int m4val;                /* bottom margin, including footer */
static int bottom;               /* last live line on page: */
static int dir;                  /* directions flag */
static char *header;             /* top of page title */
static char *footer;             /* bottom of page title */
static char *outbuf;             /* lines to be filled go here */
static int inline, outline;
static int outp;
static int para1;                 /* excess paragraph indentation of 1st line of paragraph */
static int paraphase;

/* ----------------------------- */
/* keep dot commands if keep not 0.
 */
roff(keep)
int keep;
{
    register i;
    char inbuf[LLIM];
    int c, ncline, msgline, lastlsav;
    char *sp;
    char *getline();
    char temp[81];
    char buf1[LLIM];
    char buf2[LLIM];
    char buf3[LLIM];


/* the large buffers are automatic so that they do not occupy permanent
 * storage.  The pointers to them are global within this file.
 */
    header = &buf1[0];
    footer = &buf2[0];
    outbuf = &buf3[0];

/* don't retain memory of state after previous call */
    fill = YES;
    lsval = 1;
    inval = 0;
    rmval = PAGEWIDTH;
    ceval = 0;
    ulval = 0;
    curpag = 0;
    newpag = 1;
    pageline = 0;
    plval = PAGELINES;
    m1val = 2;
    m2val = 3;
    m3val = 2;
    m4val = 3;
    bottom = plval - m3val - m4val; /* invariant */
    header[0] = '\0';
    footer[0] = '\0';


/* Old lines are deleted in blocks of 100 because deltp is slow for large
 * documents.  Its execution time is independent of the number of
 * lines deleted.
 */
    inline = 1;
    lastlsav = lastl;
    msgline = 0;
    outline = lastl;
    outbuf[0] = '\0';
    outp = 0;
    paraphase = 0;    /* expecting first line of paragraph */

    while (inline <= lastlsav) {
        strcpy(inbuf,getline(inline));
        if ( (inline + msgline ) == 1 || (inline % 100) == 0) {
            sprintf(temp,"Reforming per dot commands.  Line %d", inline+msgline);
            putmess(temp);
        }
        if ((inline % 100) == 0) {
            deltp(1,100);
            inline -= 100;
            lastlsav -= 100;
            outline -= 100;
            msgline += 100;
        }
        if (inline + msgline == cline)
            ncline = outline - lastlsav;
        inline++;

/* it is necessary to flush the output buffer before dot commands
 * if they are left in the output.
 */
        if (inbuf[0] == '.' && isalpha(inbuf[1]) ) {
            if (keep) {
                putwrd("");   /* flush the output buffer */
                inject(outline++,inbuf);  /* leave the dot commands in for further editing */
            }
            command(inbuf);
        }
        else {
            proctext(inbuf);
        }
    }
    putwrd("");   /* round it out */
    space(10000);

    if (lastlsav > 0)
        deltp(1,lastlsav);  /* delete the last of the old */

    plast = -1;
    moveline (ncline-cline);  /* go back to the original point, which is not now at the same line number */
    blankedmess = YES;
    return;
}





/* perform formatting command */

command(buf)
char *buf;
{
    int val, spval;
    char *argtyp;

    val = getval(buf,argtyp);  /* get numerical argument and type */
/* begin page */
    if (lookup(buf, "bp")) {
        if (pageline > 0)
            space(10000);   /* maxint overflows */
        set(&curpag, val, argtyp, curpag+1, -MAXINT, MAXINT);
        newpag = curpag;
    }
/* break */
    else if (lookup(buf,"br"))
        putwrd("");
/* center */
    else if (lookup(buf,"ce")) {
        putwrd("");
        set(&ceval,val,argtyp,1,0,MAXINT);
    }
/* fill */
    else if (lookup(buf,"fi")) {
        putwrd("");
        fill = YES;
    }
/* footnote */
    else if (lookup(buf,"fo")) {
        putwrd("");
        strcpy(footer,buf+3);
    }
/* header */
    else if (lookup(buf,"he")) {
        putwrd("");
        strcpy(header,buf+3);
        if (header[0] == '\0')
            strcpy(header," ");   /* activate pagination */
    }
/* indent */
    else if (lookup(buf,"in")) {
        set(&inval, val, argtyp, 0, 0, rmval-1);
    }
    else if (lookup(buf,"ls"))
        set(&lsval,val,argtyp,1,1,MAXINT);

/* need at least n lines before new page */
    else if (lookup(buf, "ne")) {
        if (val + pageline - 1  > bottom)
            space(10000);
    }
    else if (lookup(buf,"nf")) {
        putwrd("");  /* runout */
        fill = NO;
    }
    else if (lookup(buf,"pl")) {
        set(&plval, val, argtyp, PAGELINES, m1val+m2val+m3val+m4val+1, MAXINT);
        bottom=plval-m3val-m4val;
    }
    else if (lookup(buf,"rm")) {
        putwrd("");
        set(&rmval, val, argtyp, PAGEWIDTH, inval+1, MAXINT);
    }
    else if (lookup(buf,"sp")) {
        set(&spval,val,argtyp,1,0,MAXINT);
        putwrd("");
        space(spval);
    }
    else if (lookup(buf,"ul"))
        set(&ulval,val,argtyp,0,1,MAXINT);
    else
        return;    /* ignore unknown commands */
}


/* lookup routine for commands */

lookup(buf,string)
char *buf, *string;
{
    return (buf[1] == string[0]) && (buf[2] == string[1]);
}


/* evaluate optional numeric argument */

getval(buf,argtyp)
char *buf, *argtyp;
{
    int i;

    i = 3;
/* ..find argument.. */
    while (buf[i] == ' ' || buf[i] == '\t')
        ++i;
    *argtyp = buf[i];
    if (*argtyp == '+' || *argtyp=='-')
        i++;

    return(atoi(buf+i));
}


/* set parameter and check range */

set(param,val,argtyp,defval,minval,maxval)
int *param,val,defval,minval,maxval;
char *argtyp;
{
    if (*argtyp == '\0')
        *param = defval;
    else if (*argtyp == '+')
        *param += val;
    else if (*argtyp == '-')
        *param -= val;
    else
        *param = val;

    *param = min(*param,maxval);
    *param = max(*param,minval);
}

/* ------------- reformat text lines ------------------- */

proctext(inbuf)
char *inbuf;
{
    char wrdbuf[LLIM];
    char *nl;
    int save, inpoint;
    int i, j;


    for (inpoint = 0; inbuf[inpoint] == ' '; inpoint++)
        ;
    if (fill) {

/* determine additional indentation for first line of paragraph, if any */
        if (inbuf[0] == '\0') {
            paraphase = 0;    /* looking for 1st line of paaragraph */
        }
        else if (paraphase == 0) {
            paraphase = 1;
            if (inline <= lastl) {   /* use preceeding value if last line */
                nl = getline(inline);   /* next line */
                for (j = 0; nl[j] == ' '; j++)
                    ;
                para1 = max(inpoint - j, 0);
            }
        }
    }
    if (ulval > 0) {
        underl(inbuf, wrdbuf, LLIM);
        ulval--;
    }
    if (ceval > 0) {   /* nofill lines can be centered */
        paraphase = 0;
        putwrd("");
        save = inval;
        inval = max( inval + (rmval - inval)/2 - width(inbuf+inpoint)/2 -1, 0);
        put(inbuf+inpoint);
        inval = save;
        ceval--;
        return;
    }
    if (!fill) {
        paraphase = 0;
        putwrd("");
        put(inbuf);
    }
    else if (inbuf[0] == '\0') {
        putwrd("");
        put(inbuf);
    }
    else {
        while (getwrd(inbuf, &inpoint, wrdbuf) > 0)
            putwrd(wrdbuf);
    }
    return;
}


/* put out line with proper spacing and indenting */

put(buf)
char *buf;
{
    register i, j;
    char temp[LLIM];

    if (pageline == 0 || pageline > bottom)
        if (header[0] != '\0')
            phead();

    i = 0;
    while (i < inval)   /* indent */
        temp[i++] = ' ';

/* optional extra indentation for first line of paragraph.
 */
    if (paraphase == 1) {
        for (j = 0; j < para1; j++)
            temp[i++] = ' ';  /* i continued from preceeding loop */
        paraphase = 2;
    }

    strcpy(temp + i, buf);
    if (strlen(temp) >= LLIM-1)
        cerr(130);
    inject(outline++, temp);

    if (header[0] != '\0')
        skip(min(lsval-1, bottom-pageline));
    else
        skip(lsval-1);

    pageline += lsval;
    if (pageline > bottom && header[0] != '\0')
        pfoot();
    return;
}


/* put out page header */

phead()
{
    curpag = newpag++;
    if (m1val > 0) {
        skip(m1val-1);
        puttl(header,curpag);
    }
    skip(m2val);
    pageline = m1val + m2val + 1;
    return;
}


/* put out page footer */

pfoot()
{
    skip(m3val);
    if (m4val > 0) {
        puttl(footer,curpag);
        skip(m4val-1);
    }
}


/* put out title line with optional page number */

puttl(buf,pageno)
char *buf;
int pageno;
{
    char tbuf[LLIM];
    int i, j;

    j = 0;
    for (i = 0; buf[i] != '\0'; i++) {
        if (buf[i] == PAGECHAR) {
            j += sprintf(tbuf+j,"%*d",1,pageno);
        }
        else {
            tbuf[j++] = buf[i];
        }
    }
    tbuf[j] = '\0';
    inject(outline++, tbuf);
    return;
}


/* space n lines, or to bottom of page for large n */

space(n)
int n;
{
    putwrd("");
    if (pageline > bottom)
        return;

    if (pageline == 0)
        phead();

    skip(min(n,bottom+1-pageline));
    pageline += n;
    if (pageline > bottom)
        pfoot();
}


/* output n blank lines */

skip(n)
int n;
{
    while ((n--) > 0)
        inject(outline++, "");
    return;
}


/* get non-blank word from in[i] into out.
 * increment *i.
 */

getwrd(in, inx, out)
char *in, *out;
int *inx;
{
    register i, j;
    char c;

    i = *inx;
    while ((in[i]  == ' ') || (in[i] == '\t'))
        i++;

    j = 0;
    while ( ( c = (out[j++] = in[i++]))  > ' ' || c == '\b')
        ;
    out[--j] = '\0';
    *inx = i-1;        /* return new index  */
    return(j);    /* return length of word */
}


/* put a word in outbuf; includes margin justification */

putwrd(wrdbuf)
char *wrdbuf;
{
    static int outwds, outw;
    int sw, w, nextra, i;
    char cl;

    if (wrdbuf[0] == '\0') {      /* runout */
        if (outp > 0) {
            outbuf[outp] = '\0';
            put(outbuf);
        }
        outp = outw = outwds = 0;
        outbuf[0] = '\0';
    }
    else {
        w = width(wrdbuf);
        sw = strlen(wrdbuf);

/* two spaces between sentences */
        if ( (outp >= 3) && (outbuf[outp-1] == ' ') ) {
            cl = outbuf[outp-2];
            if ( (cl == ':') || (cl == '.') || (cl == '?') || (cl == '!') || (cl == ';') ) {
                if (isupper(wrdbuf[0])) {
                    outbuf[outp++] = ' ';
                    outw += 1;
                }
            }
        }
        i = inval;
        if (paraphase == 1)
            i += para1;
        if ( outp > 0 && (outw + w + i) > rmval || sw + outp + 1 >= LLIM ) {
/* too big */
            nextra = rmval-i-outp+1;       /* no. spaces to be inserted */
            for (i=0; i < nextra; i++)
                outbuf[outp++]=' ';
/*+++        spread(outbuf,outp-1,nextra,outwds); */
            if (nextra > 0 && outwds > 1)
/*+++     outp += nextra*/
                ;
            outbuf[outp] = '\0';
            put(outbuf);    /* write the filled line */
            outp = outw = outwds = 0;
            outbuf[0] = '\0';
        }
        strcpy(outbuf+outp, wrdbuf);
        outp += sw;
        outw += w;
        outbuf[outp++] = ' ';    /* blank between words */
        outw += 1;
        outwds++;
    }
    return;
}

/* --------- */

/* spread words to justify right margin */
/* an attempt at proportional spacing */

spread(buf, outp1, ne, outwds)
char *buf;
int outp1, ne, outwds;
{
    register nholes, i, j, nb;
    if (ne <= 0 || outwds <= 1)
        return;

    dir = 1-dir;    /* reverse direction */
    nholes = outwds-1;
    i = outp1-1;
/* leave room for NEWLINE, EOS */
    j = min(LLIM-2, i+ne);
    while (i < j)
    {
        buf[j] = buf[i];
        if (buf[i] == ' ')
        {
/* compute # of blanks */
            if (dir == 0)
                nb = (ne-1)/nholes+1;
            else
                nb = ne/nholes;
            ne -= nb;
            nholes--;
/* put blanks in buffer */
            while (nb-- > 0)
                buf[--j] = ' ';
        }
        i--;
        j--;
    }
    return;
}


/* compute printed width of character string */

width(buf)
char *buf;
{
    register i, val;

    for (val = i = 0; (buf[i]); i++)
        if (buf[i] == '\b')
            val--;
        else
            val++;
    return(val);
}




/* underline a line */

underl(buf,tbuf,size)
char *buf, *tbuf;
int size;
{
    int i, j,
    k, k1;    /* how many chars in last word */

    i = j = k = 0;
    while (j < size-2) {
        if (isspace(buf[i]) || buf[i] == '\0') {
            k1 = k;
            while (k-- > 0) {
                if (j >= size-2)
                    break;
                tbuf[j++] = '\b';
            }
            k = k1;
            while (k-- > 0) {
                if (j >= size-2)
                    break;
                tbuf[j++] = '_';
            }
        }
        if (buf[i] == '\0')
            break;
        else if (j >= size-2)
            break;
        else {
            if (buf[i] != ' ' && buf[i] != '\t')
                k++;
            else
                k = 0;
            tbuf[j++] = buf[i++];
        }
    }
    tbuf[j++] = '\0';
    strcpy(buf,tbuf);
    return;
}
/* ----------------- help display ---------------- */
helpdot()
{
    cleareop(1);
    putstr("*.bp [p]  begin new page\n .br  break\n .ce [l]  center\n");
    putstr(" .fi  fill paragraphs\n*.fo [text#]  footer\n*.he [text#]  header\n");
    putstr(" .in c  indent\n*.ls l  line spacing\n*.ne l  need l for entity\n .nf   no fill\n");
    putstr(" .pl l  lines/page\n .rm c  right margin\n*.sp l  space l lines\n");
    putstr("*.ul [l]  underline\n\n");
    putstr("[] optional.  +n, -n relative to existing value. l = line, c = col, p = page\n");
    putstr(".he activates pagination.  # becomes page no.  * irreversible.\n");
    putstr("-----------------------------------------");
    topline = 19;
    calp();
    putpage();
    return;
}

